package xin.marcher.sentinel.extension.file;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;

import java.io.FileNotFoundException;
import java.util.List;

/**
 * InitFunc 实现类，基于 File 实现，处理 dataSource 初始化逻辑
 *
 * @author marcher
 */
public class FileDataSourceInit implements InitFunc {

    @Override
    public void init() throws Exception {
        // 创建文件目录
        RuleFileUtil.mkdirIfNotExits(PersistenceRuleConstant.STORE_PATH);

        // 创建规则文件
        RuleFileUtil.createFileIfNotExits(PersistenceRuleConstant.RULES_MAP);

        // 处理流控规则逻辑
        dealFlowRules();
        // 处理降级规则
        dealDegradeRules();
        // 处理系统规则
        dealSystemRules();
        // 处理热点参数规则
        dealParamFlowRules();
        // 处理授权规则
        dealAuthRules();
    }

    /**
     * 处理流控规则逻辑
     *
     * @throws FileNotFoundException    exception
     */
    private void dealFlowRules() throws FileNotFoundException {
        // 规则文件路径
        String ruleFilePath = PersistenceRuleConstant.RULES_MAP.get(PersistenceRuleConstant.FLOW_RULE_PATH);

        // 创建流控规则的可读数据源
        ReadableDataSource<String, List<FlowRule>> flowRuleRds = new FileRefreshableDataSource<>(
                ruleFilePath, RuleListConverterUtil.flowRuleListParser
        );

        // 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时，就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRds.getProperty());

        WritableDataSource<List<FlowRule>> flowRuleWds = new FileWritableDataSource<>(
                ruleFilePath, RuleListConverterUtil.flowFuleEnCoding
        );

        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        // 这样收到控制台推送的规则时，Sentinel 会先更新到内存，然后将规则写入到文件中.
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWds);
    }

    /**
     * 处理降级规则
     *
     * @throws FileNotFoundException    exception
     */
    private void dealDegradeRules() throws FileNotFoundException {
        // 规则文件路径
        String degradeRuleFilePath = PersistenceRuleConstant.RULES_MAP.get(PersistenceRuleConstant.DEGRAGE_RULE_PATH);

        // 创建降级规则的可读数据源
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRds = new FileRefreshableDataSource<>(
                degradeRuleFilePath, RuleListConverterUtil.degradeRuleListParse
        );

        // 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时，就会更新规则到内存
        DegradeRuleManager.register2Property(degradeRuleRds.getProperty());


        WritableDataSource<List<DegradeRule>> degradeRuleWds = new FileWritableDataSource<>(
                degradeRuleFilePath, RuleListConverterUtil.degradeRuleEnCoding
        );

        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        // 这样收到控制台推送的规则时，Sentinel 会先更新到内存，然后将规则写入到文件中.
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWds);
    }

    /**
     * 处理系统规则
     *
     * @throws FileNotFoundException    exception
     */
    private void dealSystemRules() throws FileNotFoundException {
        // 规则文件路径
        String systemRuleFilePath = PersistenceRuleConstant.RULES_MAP.get(PersistenceRuleConstant.SYSTEM_RULE_PATH);

        // 创建系统规则的可读数据源
        ReadableDataSource<String, List<SystemRule>> systemRuleRds = new FileRefreshableDataSource<>(
                systemRuleFilePath, RuleListConverterUtil.sysRuleListParse
        );

        // 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时，就会更新规则到内存
        SystemRuleManager.register2Property(systemRuleRds.getProperty());


        WritableDataSource<List<SystemRule>> systemRuleWds = new FileWritableDataSource<>(
                systemRuleFilePath, RuleListConverterUtil.sysRuleEnCoding
        );

        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        // 这样收到控制台推送的规则时，Sentinel 会先更新到内存，然后将规则写入到文件中.
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWds);
    }

    /**
     * 处理热点参数规则
     *
     * @throws FileNotFoundException    exception
     */
    private void dealParamFlowRules() throws FileNotFoundException {
        // 规则文件路径
        String paramFlowRuleFilePath = PersistenceRuleConstant.RULES_MAP.get(PersistenceRuleConstant.HOT_PARAM_RULE);

        // 创建热点参数规则的可读数据源
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRds = new FileRefreshableDataSource<>(
                paramFlowRuleFilePath, RuleListConverterUtil.paramFlowRuleListParse
        );

        // 将可读数据源注册至 FlowRuleManager 这样当规则文件发生变化时，就会更新规则到内存
        ParamFlowRuleManager.register2Property(paramFlowRuleRds.getProperty());


        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWds = new FileWritableDataSource<>(
                paramFlowRuleFilePath, RuleListConverterUtil.paramRuleEnCoding
        );

        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        // 这样收到控制台推送的规则时，Sentinel 会先更新到内存，然后将规则写入到文件中.
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWds);
    }

    /**
     * 处理授权规则
     *
     * @throws FileNotFoundException    exception
     */
    private void dealAuthRules() throws FileNotFoundException {
        // 规则文件路径
        String authFilePath = PersistenceRuleConstant.RULES_MAP.get(PersistenceRuleConstant.AUTH_RULE_PATH);


        // 创建授权规则的读数据源
        ReadableDataSource<String, List<AuthorityRule>> authRuleRds = new FileRefreshableDataSource<>(
                authFilePath, RuleListConverterUtil.authorityRuleParse
        );


        // 将可读数据源注册至 FlowRuleManager 这样当规则文件发生变化时，就会更新规则到内存
        AuthorityRuleManager.register2Property(authRuleRds.getProperty());

        // 创建授权规则的写数据源
        WritableDataSource<List<AuthorityRule>> authRuleWds = new FileWritableDataSource<>(
                authFilePath, RuleListConverterUtil.authorityEncoding
        );

        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        // 这样收到控制台推送的规则时，Sentinel 会先更新到内存，然后将规则写入到文件中.
        WritableDataSourceRegistry.registerAuthorityDataSource(authRuleWds);
    }
}
